using System;
using System.Collections.Generic;
using System.Text;
using Colman.Rpg.Messaging;
using Colman.Rpg.Npc;

namespace Colman.Rpg.Server.Workflow
{
    /// <summary>
    /// Handles the NPC messages
    /// </summary>
    public static class NpcWorkflowHandler
    {
        private static Dictionary<string, Monster> ongoingNpcBattles = new Dictionary<string, Monster>();
        private static Random random = new Random();

        /// <summary>
        /// Creates Npc response
        /// </summary>
        /// <param name="e">Holds the requested message</param>
        public static void CreateResponses(WorkflowHandlerArgs e)
        {
            Player player = (e.Request.UserName == null ? null : PlayersCache.Get(e.Request.UserName));

            if (e.Request is FightMonsterRequest)
            {
                if (!ongoingNpcBattles.ContainsKey(player.Name))
                {
                    FightMonsterResponse response = new FightMonsterResponse(player);
                    e.Responses.Add(response);
                    ongoingNpcBattles.Add(player.Name, ((FightMonsterResponse)response).Monster);
                }
            }
            else if (e.Request is FightRequest)
            {
                if (ongoingNpcBattles.ContainsKey(player.Name))
                {
                    e.Responses.AddRange(CreateFightResponse(e.Request));
                }
            }
            else if (e.Request is CreateWorldRequest)
            {
                e.Responses.Add(new CreateWorldResponse(new World(100, 0.1)));
            }
        }

        private static IEnumerable<ResponseMessage> CreateFightResponse(RequestMessage request)
        {
            FightRequest fightRequest = (FightRequest)request;
            Character opponent = null;
            Player player = null;
            int level = int.MaxValue;
            int playerActionResult = 0, monsterActionResult = 0;

            if (ongoingNpcBattles.ContainsKey(request.UserName))
            {
                opponent = ongoingNpcBattles[request.UserName];
                player = PlayersCache.Get(request.UserName);
                level = player.Level;

                switch (fightRequest.Action)
                {
                    case FightActionType.Flee:
                        player.FleeBattle();
                        ongoingNpcBattles.Remove(player.Name);
                        yield return new PlayerLosesResponse(player);
                        PlayersCache.Set(player);

                        yield break; // You ran away. No more fighting for you.

                    case FightActionType.Attack:
                        playerActionResult = player.Attack(opponent);
                        break;
                    case FightActionType.Defend:
                        playerActionResult = player.Defend(opponent);
                        break;
                }

                monsterActionResult = opponent.Attack(player);
            }

            if (player.HP == 0)
            {
                player.HP = player.MaxHP;
                player.LoseBattle();
                ongoingNpcBattles.Remove(player.Name);
                yield return new PlayerLosesResponse(player);
            }
            else if (opponent.HP == 0)
            {
                int gainedXP = player.WinAgainst(opponent);
                ongoingNpcBattles.Remove(player.Name);

                // Most of the time you win nothing. Sometimes you win a lot.
                int gainedGold = Convert.ToInt32(Math.Floor(Math.Sqrt(Math.Max(0, random.Next(-4, 4)))) * (gainedXP * random.Next(8, 12) / 10.0));
                player.Gold += gainedGold;

                yield return new PlayerWinsResponse(player, gainedXP, gainedGold);

                if (player.Level > level)
                {
                    int pointsOwedForLevellingUp = player.Level;
                    player.AbilityPointsOwed += pointsOwedForLevellingUp;

                    yield return new PlayerLevelledUpResponse(player, pointsOwedForLevellingUp);
                }
            }
            else
            {
                yield return new FightResponse(player, fightRequest.Action, playerActionResult,
                    opponent, FightActionType.Attack, monsterActionResult);
            }

            PlayersCache.Set(player);
        }
    }
}
